from turtle import position
from ursina import *
import math


def update():
    # t 更新频率
    global t, radius_moon
    # pause_flag 暂停标识，通过判定该值来确定是否处于暂停状态，决定是否暂停模拟动画
    global pause_flag
    # speed_step 步进速度，通过修改该值来向前或向后加速模拟动画
    global speed_step
    # vall_points 记录星球球体们的运动轨迹
    global ball_points

    global star_center
    global star_pos

    if not pause_flag:
        t = t + speed_step
        angle = math.pi*40/180
        radius_moon += -0.01
        # 设置球体的公转

        sun.x = math.cos(t)*star_pos['sun']
        sun.z = math.sin(t)*star_pos['sun']

        moon.x = math.cos(radius_moon/0.28) * 0.7
        moon.z = math.sin(radius_moon/0.28) * 0.7

        if star_center == 'mercury':
            mercury.x = math.cos(t)*star_pos['mercury']
            mercury.z = math.sin(t)*star_pos['mercury']
        else:
            # *4.13deg/day 公转角速度
            mercury.x = math.cos(t*4.13) * (star_pos['mercury'] + sun.x)
            mercury.z = math.sin(t*4.13) * (star_pos['mercury'] + sun.z)

        if star_center == 'venus':
            venus.x = math.cos(t) * star_pos['venus']
            venus.z = math.sin(t) * star_pos['venus']
        else:
            # *1.60deg/day 公转角速度
            venus.x = math.cos(t*1.60+angle) * (star_pos['venus'] + sun.x)
            venus.z = math.sin(t*1.60+angle) * (star_pos['venus'] + sun.z)

        if star_center == 'earth':
            earth.x = math.cos(t) * star_pos['earth']
            earth.z = math.sin(t) * star_pos['earth']
        else:
            # *0.98deg/day 公转角速度
            earth.x = math.cos(t*0.98+angle*2) * (star_pos['earth'] + sun.x)
            earth.z = math.sin(t*0.98+angle*2) * (star_pos['earth'] + sun.z)

        if star_center == 'mars':
            mars.x = math.cos(t) * star_pos['mars']
            mars.z = math.sin(t) * star_pos['mars']
        else:
            # *0.52deg/day 公转角速度
            mars.x = math.cos(t*0.52+angle*3) * (star_pos['mars'] + sun.x)
            mars.z = math.sin(t*0.52+angle*3) * (star_pos['mars'] + sun.z)

        if star_center == 'jupiter':
            jupiter.x = math.cos(t) * star_pos['jupiter']
            jupiter.z = math.sin(t) * star_pos['jupiter']
        else:
            # *0.08deg/day 公转角速度
            jupiter.x = math.cos(t*0.08+angle*4) * (star_pos['jupiter'] + sun.x)
            jupiter.z = math.sin(t*0.08+angle*4) * (star_pos['jupiter'] + sun.z)

        if star_center == 'saturn':
            saturn.x = math.cos(t) * star_pos['saturn']
            saturn.z = math.sin(t) * star_pos['saturn']
        else:
            # 0.03deg/day 公转角速度
            saturn.x = math.cos(t*0.03+angle*5) * (star_pos['saturn'] + sun.x)
            saturn.z = math.sin(t*0.03+angle*5) * (star_pos['saturn'] + sun.z)

        if star_center == 'uranus':
            uranus.x = math.cos(t) * star_pos['uranus']
            uranus.z = math.sin(t) * star_pos['uranus']
        else:
            # 0.011deg/day 公转角速度
            uranus.x = math.cos(t*0.011+angle*6) * (star_pos['uranus'] + sun.x)
            uranus.z = math.sin(t*0.011+angle*6) * (star_pos['uranus'] + sun.z)

        if star_center == 'neptune':
            neptune.x = math.cos(t) * star_pos['neptune']
            neptune.z = math.sin(t) * star_pos['neptune']
        else:
            # 0.0059deg/day 公转角速度
            neptune.x = math.cos(t*0.0059+angle*7) * (star_pos['neptune'] + sun.x)
            neptune.z = math.sin(t*0.0059+angle*7) * (star_pos['neptune'] + sun.z)

        # 球体的自转
        sun.rotation_y += time.dt*speed_step*1000*7.2
        mercury.rotation_y += time.dt*speed_step*1000*0.0032
        earth.rotation_y += time.dt*speed_step*1000*0.46
        venus.rotation_y += time.dt*speed_step*1000*(-0.00182)
        mars.rotation_y += time.dt*speed_step*1000*0.24
        jupiter.rotation_y += time.dt*speed_step*1000*1.266
        saturn.rotation_y += time.dt*speed_step*1000*1.030
        uranus.rotation_y += time.dt*speed_step*1000*2.62
        neptune.rotation_y += time.dt*speed_step*1000*2.70
        # 只有在需要显示轨迹线时才收集点集
        if step_line.line_visible:
            ball_points.append((sun.x, 0, sun.z))
            ball_points.append((mercury.x, 0, mercury.z))
            ball_points.append((earth.x, 0, earth.z))
            ball_points.append((venus.x, 0, venus.z))
            ball_points.append((mars.x, 0, mars.z))
            ball_points.append((jupiter.x, 0, jupiter.z))
            ball_points.append((saturn.x, 0, saturn.z))
            ball_points.append((uranus.x, 0, uranus.z))
            ball_points.append((neptune.x, 0, neptune.z))
            step_line.set_points(ball_points)
        # 限制点集的大小，避免数量过大导致程序卡顿
        if len(ball_points) > 25400:
            ball_points = ball_points[-25400:]


# 自定义一个天空类（也就是星空背景），继承实体Entity
class Sky(Entity):
    def __init__(self):
        super().__init__(
            model = 'sphere',
            texture = 'textures/starsMap3.jpg',
            parent = scene,
            scale = 400,
            double_sided = True
        )


# 自定义一个轨迹线类，继承实体Entity
class stepLines(Entity):
    def __init__(self):
        # 初始化轨迹节点
        super().__init__()
        self.verts = []
        self.verts_points = Entity(model=Mesh(vertices=self.verts, mode='point', thickness=2, ), color=color.green)
        self.line_visible = False

    def set_points(self, mverts):
        # 更新轨迹节点
        self.verts = mverts
        self.verts_points.model = Mesh(vertices=self.verts, mode='point', thickness=2)
        self.verts_points.color = color.green

    def show(self):
        # print(len(self.verts))
        self.verts_points.visible = True
        self.line_visible = True

    def hide(self):
        # 设置实体可见性
        self.verts_points.visible = False
        self.line_visible = False
        # 清除实体节点--真正的消失
        self.verts_points.clear_model_nodes()
        # 清除点集--清除后下次显示会重新跟随星球球体绘制，不清除的话将会显示上一次的绘制。建议清除点集，这样在绘制时能得到较好的画面表现
        self.verts.clear()
        ball_points.clear()


# 创建app对象
app = Ursina(vsync=True, borderless=False)
window.title = '3D-solar-system'
# 初始化天空
sku = Sky()
# 初始化轨迹线
step_line = stepLines()
# 初始化相机视角
EditorCamera()


# 键盘响应事件
def input(key):
    global pause_flag
    global speed_step
    # print(key)
    if key == 'q up' and not pause_flag:
        speed_step = speed_step - 0.01
    elif key == 'e up' and not pause_flag:
        speed_step = speed_step + 0.01
    elif key == 'p up':
        pause_flag = not pause_flag
    elif key == 'g up':
        # g键显示轨迹线,show
        if not step_line.line_visible:
            step_line.show()
    elif key == 'h up':
        # h键隐藏轨迹线,hide
        step_line.hide()
    elif key == 't up':
        text_tip.visible = not text_tip.visible


# 下列为各个球体的点击回调函数
# 球体相对位置的转换，所有变换以太阳位置为基准
def on_sun_clicked():
    print("click sun")
    global star_center
    global star_pos
    # 记录作为中心的星星
    star_center = 'sun'
    # 同时以太阳(0,0)为基准,改变其他星星的相对半径，也就是保持其到到太阳的半径不变
    planet_params = {
        'sun': {'radius': 0},
        'mercury': {'radius': 2.5},
        'venus': {'radius': 3.5},
        'earth': {'radius': 4.5},
        'mars': {'radius': 5.5},
        'jupiter': {'radius': 7.5},
        'saturn': {'radius': 9.5},
        'uranus': {'radius': 11.5},
        'neptune': {'radius': 13.5},
    }
    for planet, params in planet_params.items():
        star_pos[planet] = params['radius']
    # 清除一下轨迹线，避免画面过于混乱
    step_line.hide()


def create_planet_click_handler(planet_name, radius):
    def handler():
        print(f"click {planet_name}")
        global star_center, star_pos
        star_center = planet_name
        pos_this = radius
        planet_params = {
            'sun': {'radius': 0},
            'mercury': {'radius': 2.5},
            'venus': {'radius': 3.5},
            'earth': {'radius': 4.5},
            'mars': {'radius': 5.5},
            'jupiter': {'radius': 7.5},
            'saturn': {'radius': 9.5},
            'uranus': {'radius': 11.5},
            'neptune': {'radius': 13.5},
        }
        for planet, params in planet_params.items():
            star_pos[planet] = params['radius'] - pos_this
        step_line.hide()
    return handler

on_mercury_clicked = create_planet_click_handler('mercury', 2.5)
on_venus_clicked = create_planet_click_handler('venus', 3.5)
on_earth_clicked = create_planet_click_handler('earth', 4.5)
on_mars_clicked = create_planet_click_handler('mars', 5.5)
on_jupiter_clicked = create_planet_click_handler('jupiter', 7.5)
on_saturn_clicked = create_planet_click_handler('saturn', 9.5)
on_uranus_clicked = create_planet_click_handler('uranus', 11.5)
on_neptune_clicked = create_planet_click_handler('neptune', 13.5)



# 初始化各个球体的实体，注册回调函数(on_click)、碰撞器(collider)，设置大小关系(scale)
# 太阳
sun = Entity(model="sphere",  scale=3, texture="textures/2k_sun.jpg", collider='box',
             on_click=on_sun_clicked)
# 水星
mercury = Entity(model='obj/Mercury 1K.obj',scale=0.5, texture = "textures/Diffuse_1K.png", collider='box',
                 on_click=on_mercury_clicked)
# 金星
venus = Entity(model='sphere', scale=0.7, texture = 'textures/2k_venus_surface.jpg', collider='box',
               on_click=on_venus_clicked)
# 地球
earth = Entity(model='sphere', scale=0.8, texture = "textures/earth albedo.jpg", collider='box',
               on_click=on_earth_clicked)
# 月球
moon = Entity(parent=earth, model="sphere", texture="textures/2k_moon.jpg", scale=0.16)
# 火星
mars = Entity(model='obj/Mars 2K.obj',scale=0.05, texture = 'textures/mars.png', collider='box',
              on_click=on_mars_clicked)
# 木星
jupiter = Entity(model='sphere',scale=2, texture = 'textures/jupitermap.jpg', collider='box',
                 on_click=on_jupiter_clicked)
# 土星
saturn = Entity(model= 'sphere', scale=1.5, texture = 'textures/saturnmap.jpg', collider='box',
                on_click=on_saturn_clicked)
# 天王星
uranus = Entity(model='sphere', scale=1.1, texture = 'textures/uranusmap.jpg', collider='box',
                on_click=on_uranus_clicked)
# 海王星
neptune = Entity(model='sphere',  scale=1, texture = 'textures/neptunemap.jpg', collider='box',
                 on_click=on_neptune_clicked)

# 球体的倾角
sun.rotation_x = 0
mercury.rotation_x = 0
earth.rotation_x = 27.4
venus.rotation_x = 177.3
mars.rotation_x = 25.2
jupiter.rotation_x = 3.1
saturn.rotation_x = 26.7
uranus.rotation_x = 97.8
neptune.rotation_x = 28.3


'''
操作说明
E/Q  ：向前/向后 改变动画速度
P   ：暂停
G/H  ：展示/隐藏 轨迹线
鼠标右键（按住）：改变视角
鼠标左键（点击）：将点击的星球放到中心，视角以该球为中心
本系统为三维2轴动画，没有Y分量的位移，所以可能出现穿模现象，这属于正常现象
'''
descr = dedent('''
      <scale:1.3><white>操作说明(<orange>T <white>显示/隐藏 该提示):
      <scale:1>
      <orange>E/Q  <default>向前/向后 改变动画速度<orange>  <orange> G/H  <default>展示/隐藏 轨迹线
      <orange>P  <default>暂停 <orange><orange>  鼠标滚轮  <default>视角前进/后退 <orange>
      <orange>鼠标右键（按住拖动）  <default>改变视角 <orange><orange> 鼠标右键（按住）+ W/S/A/D  <default>移动视图 <orange>
      <orange>鼠标左键（点击）  <default>将点击的星球放到中心，视角以该球为中心 <orange>
      ''').strip()
text_tip = Text(text=descr, origin=(-0.5, -2.7), background=True, font='font/simsun.ttc')
text_tip.x = -0.879 / 2
Text.default_resolution = 1080 * Text.size
# 初始化一些全局变量
radius_moon = 1
t = -math.pi
pause_flag = False
speed_step = 0.02
ball_points = []
star_center = 'sun'
star_pos = {
    'sun': 0,
    'mercury': 2.5,
    'venus': 3.5,
    'earth': 4.5,
    'mars': 5.5,
    'jupiter': 7.5,
    'saturn': 9.5,
    'uranus': 11.5,
    'neptune': 13.5
}
# 运行引擎
app.run()

